package core

import (
	"alertmanagerWebhook/global"
	"fmt"
	"strings"
	"time"

	"github.com/gomodule/redigo/redis"
)

// 连接选项配置
var redisDialOptions = []redis.DialOption{
	redis.DialConnectTimeout(5 * time.Second),
	redis.DialReadTimeout(3 * time.Second),
	redis.DialWriteTimeout(3 * time.Second),
}

func ConnRedis() (redis.Conn, error) {
	global.Logger.Infof("开始Redis连接, 模式: %s", global.Config.Redis.Mode)
	switch global.Config.Redis.Mode {
	case "cluster":
		return connectRedisCluster()
	case "master-slave":
		return connectRedisMasterSlave()
	default:
		return connectRedisSingle()
	}
}

func connectRedisSingle() (redis.Conn, error) {
	addr := global.Config.Redis.RedisAddr()
	global.Logger.Debugf("尝试连接Redis单节点: %s", addr)

	conn, err := redis.Dial("tcp", addr, append(redisDialOptions,
		redis.DialPassword(global.Config.Redis.RedisPassword))...)
	if err != nil {
		global.Logger.Errorf("连接Redis单节点失败(%s): %v", addr, err)
		return nil, fmt.Errorf("连接Redis单节点失败(%s): %v", addr, err)
	}

	global.Logger.Infof("成功连接Redis单节点: %s", addr)
	return conn, nil
}

func connectRedisMasterSlave() (redis.Conn, error) {
	// 保存原始配置
	origServer := global.Config.Redis.RedisServer
	origPort := global.Config.Redis.RedisPort
	defer func() {
		global.Config.Redis.RedisServer = origServer
		global.Config.Redis.RedisPort = origPort
	}()

	// 尝试主节点
	global.Logger.Debug("尝试连接Redis主节点")
	if conn, err := connectRedisSingle(); err == nil {
		global.Logger.Info("成功连接Redis主节点")
		return conn, nil
	} else {
		global.Logger.Warnf("主节点连接失败: %v", err)
	}

	global.Logger.Debug("开始尝试从节点连接")
	for _, addr := range global.Config.Redis.SlaveNodes {
		server, port := parseRedisAddr(addr)
		if server == "" || port == "" {
			continue
		}

		global.Config.Redis.RedisServer = server
		global.Config.Redis.RedisPort = port
		if conn, err := connectRedisSingle(); err == nil {
			global.Logger.Infof("成功连接Redis从节点: %s", addr)
			return conn, nil
		} else {
			global.Logger.Warnf("从节点连接失败(%s): %v", addr, err)
		}
	}

	global.Logger.Error("所有Redis主从节点连接失败")
	return nil, fmt.Errorf("无法连接Redis主从节点(主节点:%s:%s, 从节点:%v)",
		origServer, origPort, global.Config.Redis.SlaveNodes)
}

func connectRedisCluster() (redis.Conn, error) {
	if len(global.Config.Redis.ClusterNodes) == 0 {
		return nil, fmt.Errorf("集群模式需要配置clusterNodes")
	}

	// 尝试所有集群节点
	var lastErr error
	global.Logger.Debug("开始尝试Redis集群节点连接")
	for _, addr := range global.Config.Redis.ClusterNodes {
		conn, err := redis.Dial("tcp", addr, append(redisDialOptions,
			redis.DialPassword(global.Config.Redis.RedisPassword))...)
		if err == nil {
			global.Logger.Infof("成功连接Redis集群节点: %s", addr)
			return conn, nil
		}
		global.Logger.Warnf("Redis集群节点连接失败(%s): %v", addr, err)
		lastErr = err
	}

	global.Logger.Error("所有Redis集群节点连接失败")
	return nil, fmt.Errorf("连接Redis集群失败: %v (尝试节点: %v)", lastErr, global.Config.Redis.ClusterNodes)
}

func parseRedisAddr(addr string) (server, port string) {
	parts := strings.Split(addr, ":")
	if len(parts) == 2 {
		return parts[0], parts[1]
	}
	return "", ""
}

// 新增函数：获取可写连接(主节点)
func GetWritableRedisConn() (redis.Conn, error) {
	global.Logger.Info("获取可写Redis连接(主节点)")
	switch global.Config.Redis.Mode {
	case "cluster":
		return connectRedisCluster()
	case "master-slave":
		return connectRedisMaster(true)
	default:
		return connectRedisSingle()
	}
}

// 新增函数：获取只读连接(从节点)
func GetReadOnlyRedisConn() (redis.Conn, error) {
	global.Logger.Info("获取只读Redis连接(从节点)")
	switch global.Config.Redis.Mode {
	case "cluster":
		return connectRedisCluster()
	case "master-slave":
		return connectRedisMaster(false)
	default:
		return connectRedisSingle()
	}
}

// 重构主从连接逻辑
func connectRedisMaster(forceMaster bool) (redis.Conn, error) {
	// 保存原始配置
	origServer := global.Config.Redis.RedisServer
	origPort := global.Config.Redis.RedisPort
	defer func() {
		global.Config.Redis.RedisServer = origServer
		global.Config.Redis.RedisPort = origPort
	}()

	// 强制主节点时，直接尝试主节点配置
	if forceMaster {
		global.Logger.Debug("强制连接Redis主节点")
		if conn, err := connectRedisSingle(); err == nil {
			if !isReadOnly(conn) {
				return conn, nil
			}
			conn.Close()
			return nil, fmt.Errorf("配置的主节点实际上是只读从节点")
		}
		return nil, fmt.Errorf("无法连接配置的主节点")
	}

	// 非强制主节点时，先尝试主节点，再尝试从节点
	global.Logger.Debug("尝试连接Redis主节点")
	if conn, err := connectRedisSingle(); err == nil {
		if !isReadOnly(conn) {
			return conn, nil
		}
		conn.Close()
	}

	// 尝试从节点
	global.Logger.Debug("开始尝试从节点连接")
	for _, addr := range global.Config.Redis.SlaveNodes {
		server, port := parseRedisAddr(addr)
		if server == "" || port == "" {
			continue
		}

		global.Config.Redis.RedisServer = server
		global.Config.Redis.RedisPort = port
		if conn, err := connectRedisSingle(); err == nil {
			if isReadOnly(conn) {
				return conn, nil
			}
			conn.Close()
		}
	}

	return nil, fmt.Errorf("无法连接Redis节点")
}

// 新增函数：检查节点是否只读
func isReadOnly(conn redis.Conn) bool {
	reply, err := redis.String(conn.Do("INFO", "replication"))
	if err != nil {
		return false
	}
	return strings.Contains(reply, "role:slave")
}
